/*
 * Copyright (C) 2021 HiHope Open Source Organization .
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *
 * limitations under the License.
 */
#include <stdio.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_errno.h"
#include "iot_gpio.h"
#include "iot_uart.h"
#include "hi_io.h"
#include "hi_pwm.h"

#include "ssd1306.h"

#define IOT_PWM_PORT_PWM2   2
#define IOT_PWM_BEEP_GPIO   HI_IO_NAME_GPIO_5
#define IOT_PWM_BEEP_DUTY   50
#define IOT_PWM_BEEP_FREQ   4000

#define IOT_UART_IDX_1   (1)  // UNI -> Hi3861
#define UNI_CMD_LEN_MIN  (6)
#define UNI_CMD_LEN_MAX  (8)

// 你好大师兄: 5A A5 64 73 78 5A
unsigned char UNI_wakeup_uni[] = {0x5A, 0xA5, 0x64, 0x73, 0x78, 0x5A};
// 打开报警: 5A A5 04 64 6b 62 6a 5A
unsigned char UNI_TurnonAlarms[]  = {0x5A, 0xA5, 0x04, 0x64, 0x6b, 0x62, 0x6a, 0x5A};
// 关闭报警: 5A A5 04 67 62 62 6a 5A
unsigned char UNI_TurnoffAlarms[] = {0x5A, 0xA5, 0x04, 0x67, 0x62, 0x62, 0x6a, 0x5A};
// 打开台灯|请开灯|开灯|打开灯|我回来了: 5A A5 02 6F 6E 5A
unsigned char UNI_TurnOn[]     = {0x5A, 0xA5, 0x02, 0x6F, 0x6E, 0x5A};
// 关闭台灯|请关灯|关灯|睡觉了|关上灯|我出去了: 5A A5 03 6F 66 66 5A
unsigned char UNI_TurnOff[]    = {0x5A, 0xA5, 0x03, 0x6F, 0x66, 0x66, 0x5A};

typedef enum
{
    eWake_Up = 0,
    eBuzzer_On,
    eBuzzer_Off,
    eLED_On,
    eLED_Off,
    eNoMatchCmd,
} eUniCmd;

typedef struct {
    eUniCmd        cmdIndex;
    char*          cmdName;
    unsigned char  cmdLen;
    unsigned char* cmdCode;
} UniCmd;

static UniCmd g_UniCmdList[] = 
{
    {eWake_Up,    "Wake_Up",    6, UNI_wakeup_uni},
    {eBuzzer_On,  "Buzzer_On",  8, UNI_TurnonAlarms},
    {eBuzzer_Off, "Buzzer_Off", 8, UNI_TurnoffAlarms},
    {eLED_On,     "LED_On",     6, UNI_TurnOn},
    {eLED_Off,    "LED_Off",    7, UNI_TurnOff},

    {eNoMatchCmd, "NoMatchCmd", 0, NULL},
};
#define UniCmdCnt (sizeof(g_UniCmdList)/sizeof(g_UniCmdList[0]))

static osThreadId_t g_UniTaskId = NULL;
static int g_Uart1Inited = 0;
int Uart1Config(void)
{
    int ret = -1;
    // NOTE: in this case, UART1 only use RxD to receive signal from UNI_SOUND,
    // NOT using TxD to send anything out of Hi3861. GPIO_0 is ctrling LED.
    // IoTGpioInit(HI_IO_NAME_GPIO_0);
    // hi_io_set_func(HI_IO_NAME_GPIO_0, HI_IO_FUNC_GPIO_0_UART1_TXD);

    IoTGpioInit(HI_IO_NAME_GPIO_1);
    hi_io_set_func(HI_IO_NAME_GPIO_1, HI_IO_FUNC_GPIO_1_UART1_RXD);

    IotUartAttribute uart_cfg = {115200, 8, 1, 0, 500, 500, 0};
    
    if (!g_Uart1Inited) {
        ret = IoTUartInit(IOT_UART_IDX_1, &uart_cfg);
        if (ret != 0) {
            printf("%s: IoTUartInit fail\r\n", __func__);
            return -1;
        }
        g_Uart1Inited = 1;
        return 0;
    }
    return 0;
}
void LightConfig(void)
{
    IoTGpioInit(HI_IO_NAME_GPIO_0);
    hi_io_set_func(HI_IO_NAME_GPIO_0, HI_IO_FUNC_GPIO_0_GPIO);
    IoTGpioSetDir(HI_IO_NAME_GPIO_0, IOT_GPIO_DIR_OUT);
}
void BeepConfig(void)
{
    IoTGpioInit(IOT_PWM_BEEP_GPIO);
    hi_io_set_func(IOT_PWM_BEEP_GPIO, HI_IO_FUNC_GPIO_5_PWM2_OUT);
    IoTGpioSetDir(IOT_PWM_BEEP_GPIO, IOT_GPIO_DIR_OUT);
    IoTPwmInit(IOT_PWM_PORT_PWM2);
}
static eUniCmd FindMatchCmdId(unsigned char *RemoteArray, unsigned char RemoteLen)
{
    int totalCmdCnt = UniCmdCnt;
    int i = 0;
    int j = 0;
    if ((UNI_CMD_LEN_MIN <= RemoteLen) && (RemoteLen <= UNI_CMD_LEN_MAX)) {
        for(i = 0; i < totalCmdCnt; i++ ) {
            if (g_UniCmdList[i].cmdLen == RemoteLen) {
                for(j = 0; j < RemoteLen; j++) {
                    if(RemoteArray[j] != g_UniCmdList[i].cmdCode[j]) {
                        break;  // g_UniCmdList[i] is NOT match with RemoteArray[]
                    }
                }
                if (j == RemoteLen) {  // found the match cmd
                    return g_UniCmdList[i].cmdIndex;
                }
            }
        }
    } 
    return g_UniCmdList[totalCmdCnt-1].cmdIndex;  //NoMatchCmd
}

#define BUFF_SIZE  (16)
void UartDemoTask(void)
{
    unsigned int len = 0;
    unsigned char uartReadBuff[BUFF_SIZE] = {0};
    eUniCmd matchCmd;

    printf("%s: Begin:\n", __func__);
    LightConfig();
    BeepConfig();
    if (Uart1Config() != 0) {
        printf("%s: Uart1Config() fail\r\n", __func__);
        return;
    }

    while (1) {
        memset(uartReadBuff, 0, BUFF_SIZE);
        len = IoTUartRead(IOT_UART_IDX_1, uartReadBuff, BUFF_SIZE);
        if ((UNI_CMD_LEN_MIN <= len) && (len <= UNI_CMD_LEN_MAX)) {
            matchCmd = FindMatchCmdId(uartReadBuff, len);
            printf("%s: get cmd: %s\n", __func__, g_UniCmdList[matchCmd].cmdName);

            ssd1306_Fill(Black);
            ssd1306_SetCursor(5, 5);
            ssd1306_DrawString("Unisound", Font_11x18, White);
            ssd1306_SetCursor(5, 5+18+5);
            //ssd1306_DrawString(g_UniCmdList[matchCmd].cmdName, Font_7x10, White);
            ssd1306_DrawString("Value: OK", Font_7x10, White);
            ssd1306_SetCursor(5, 5+18+5+10+5+10);
            ssd1306_DrawString("UP          DOWN", Font_7x10, White);
            ssd1306_UpdateScreen();

            switch (matchCmd) {
            case eWake_Up:
                break;
            case eBuzzer_On:
                IoTPwmStart(IOT_PWM_PORT_PWM2, IOT_PWM_BEEP_DUTY, IOT_PWM_BEEP_FREQ);
                break;
            case eBuzzer_Off:
                IoTPwmStop(IOT_PWM_PORT_PWM2);
                break;
            case eLED_On:
                IoTGpioSetOutputVal(HI_IO_NAME_GPIO_0, 1);
                break;
            case eLED_Off:
                IoTGpioSetOutputVal(HI_IO_NAME_GPIO_0, 0);
                break;
            case eNoMatchCmd:
            default:
                break;
            }

            if (matchCmd < eNoMatchCmd) {
                usleep(1000*1000);
            }
        }
        usleep(100*1000);
    }
    printf("%s: End.\n", __func__);
    g_UniTaskId = NULL;

    return;
}

void UnisoundTestEntry(void)
{
    if (g_UniTaskId) {
        return;
    }

    //osPriorityAboveNormal[32], osPriorityNormal[24]
    //{.name, .attr_bits, .cb_mem, .cb_size, .stack_mem, .stack_size, .priority, .tz_module, .reserved}
    osThreadAttr_t attr = {"UartDemoTask", 0, NULL, 0, NULL, 1024*4, 24, 0, 0};
    g_UniTaskId = osThreadNew((osThreadFunc_t)UartDemoTask, NULL, &attr);
    if (g_UniTaskId == NULL) {
        printf("[UnisoundTestEntry] Falied to create %s!\n", attr.name);
    }
}

void UnisoundTestExit(void)
{
    osThreadTerminate(g_UniTaskId);
    g_UniTaskId = NULL;
    IoTUartDeinit(IOT_UART_IDX_1);
}